/*
	Copyright (c) 2007, ANYwebcam.com Pty Ltd. All rights reserved.

	The software in this package is published under the terms of the BSD style 
	license, a copy of which has been included with this distribution in the 
	license.txt file.
*/
package com.anywebcam.reflection
{
	import com.anywebcam.reflection.ClassInfo;
	import com.anywebcam.reflection.MethodInfo;

	import flexunit.framework.TestCase;
	import flexunit.framework.TestSuite;

	public class MethodInfoTest extends TestCase
	{
		public static function suite():TestSuite
		{
			var ts:TestSuite = new TestSuite();

			var tests:Array = 
			[
				'methodWithNoParametersShouldHaveEmptyParameterArray',
				'methodWithParametersShouldReturnArrayOfParameterClasses',
				'methodWithRequiredParametersShouldReturnArrayOfParameterClasses',
				'methodWithNoReturnTypeShouldReturnNull',
				'methodWithReturnTypeShouldReturnClassOfReturnType',
				'methodShouldBeCallable',
				'accessorShouldNotBeCallable',
				'variableShouldNotBeCallable',
				'shouldBeReadableIfAccessorIsReadOnly',
				'shouldNotBeReadableIfAccessorIsWriteOnly',
				'shouldBeReadableIfAccessorIsReadWrite',
				'shouldBeWritableIfAccessorIsWriteOnly',
				'shouldNotBeWritableIfAccessorIsReadOnly',
				'shouldBeWritableIfAccessorIsReadWrite',
			];

			tests.forEach( function( methodName:String, index:int, array:Array ):void
			{
				ts.addTest( new MethodInfoTest( methodName ) );
			});

			return ts;
		}
		
		private var classInfo:ClassInfo;
		
		override public function setUp():void
		{
			classInfo = new ClassInfo( ForTestingOnly );
		}

		public function MethodInfoTest( method:String )
		{
			super( method );
		}
		
		public function methodWithNoParametersShouldHaveEmptyParameterArray():void
		{
			var methodInfo:MethodInfo = classInfo.method( 'methodWithNoParameters' );
			
			assertNotNull( 'Expecting parameters to be an Array', methodInfo.parameters );
			assertEquals( 'Expecting parameters array to have zero elements', 0, methodInfo.parameters.length );
		}
		
		public function methodWithParametersShouldReturnArrayOfParameterClasses():void
		{
			var methodInfo:MethodInfo = classInfo.method( 'methodWithParametersAndNoReturnType' );
			
			assertNotNull( 'Expecting parameters to be an Array', methodInfo.parameters );
			assertEquals( 'Expecting parameters array to have 3 elements', 3, methodInfo.parameters.length );
			assertEquals( 'Expecting parameter 0 to be String', String, methodInfo.parameters[0] );
			assertEquals( 'Expecting parameter 1 to be Number', Number, methodInfo.parameters[1] );
			assertEquals( 'Expecting parameter 2 to be Object', Object, methodInfo.parameters[2] );
		}
		
		public function methodWithRequiredParametersShouldReturnArrayOfParameterClasses():void
		{
			var methodInfo:MethodInfo = classInfo.method( 'methodWithOptionalParameters' );
			
			assertNotNull( 'Expecting requiredParameters to be an Array', methodInfo.requiredParameters );
			assertEquals( 'Expecting requiredParameters array to have 1 element', 1, methodInfo.requiredParameters.length );
			assertEquals( 'Expecting requiredParameters 0 to be Number', Number, methodInfo.requiredParameters[0] );
		}
		
		public function methodWithNoReturnTypeShouldReturnNull():void
		{
			var methodInfo:MethodInfo = classInfo.method( 'methodWithParametersAndNoReturnType' );
			
			assertNull( 'Expecting returnType to be null', methodInfo.returnType );
		}
		
		public function methodWithReturnTypeShouldReturnClassOfReturnType():void
		{
			var methodInfo:MethodInfo = classInfo.method( 'callableMethod' );
			
			assertNotNull( 'Expecting returnType to not be null', methodInfo.returnType );
			assertEquals( 'Expecting returnType to be Boolean', Boolean, methodInfo.returnType );
		}
		
		public function methodShouldBeCallable():void
		{
			var methodInfo:MethodInfo = classInfo.method( 'callableMethod' );
			
			assertTrue( 'Expecting method to be callable', methodInfo.callable );
		}
		
		public function accessorShouldNotBeCallable():void
		{
			var methodInfo:MethodInfo = classInfo.property( 'readWriteAccessor' );
			
			assertFalse( 'Expecting accessor to not be callable', methodInfo.callable );
		}
		
		public function variableShouldNotBeCallable():void
		{
			var methodInfo:MethodInfo = classInfo.property( 'publicVariable' );
			
			assertFalse( 'Expecting variable to not be callable', methodInfo.callable );
		}
		
		public function shouldBeReadableIfAccessorIsReadOnly():void
		{
			var methodInfo:MethodInfo = classInfo.property( 'readOnlyAccessor' );
			
			assertTrue( 'Expecting readOnly accessor to be readable', methodInfo.readable );
		}
		
		public function shouldNotBeReadableIfAccessorIsWriteOnly():void
		{
			var methodInfo:MethodInfo = classInfo.property( 'writeOnlyAccessor' );
			
			assertFalse( 'Expecting writeOnly accessor to NOT be readable', methodInfo.readable );			
		}
		
		public function shouldBeReadableIfAccessorIsReadWrite():void
		{
			var methodInfo:MethodInfo = classInfo.property( 'readWriteAccessor' );

			assertTrue( 'Expecting readWrite accessor to be readable', methodInfo.readable );
		}
		
		public function shouldBeWritableIfAccessorIsWriteOnly():void
		{
			var methodInfo:MethodInfo = classInfo.property( 'writeOnlyAccessor' );

			assertTrue( 'Expecting writeOnly accessor to be writable', methodInfo.writable );
		}
		
		public function shouldNotBeWritableIfAccessorIsReadOnly():void
		{
			var methodInfo:MethodInfo = classInfo.property( 'readOnlyAccessor' );

			assertFalse( 'Expecting readOnly accessor to NOT be writable', methodInfo.writable );
		}
		
		public function shouldBeWritableIfAccessorIsReadWrite():void
		{
			var methodInfo:MethodInfo = classInfo.property( 'readWriteAccessor' );

			assertTrue( 'Expecting readWrite accessor to be readable', methodInfo.writable );
		}
	}
}

internal class ForTestingOnly
{
	public var publicVariable:String;
	
	public function get readOnlyAccessor():Boolean { return true; }
	
	public function set writeOnlyAccessor( value:Boolean ):void {}
	
	private var _readWrite:Number = 10;
	public function get readWriteAccessor():Number { return _readWrite; }
	public function set readWriteAccessor( value:Number ):void { _readWrite = value; }
	
	public function callableMethod():Boolean { return true; } 
	public function methodWithNoParameters():void {}	
	public function methodWithParametersAndNoReturnType( a:String, b:Number, c:Object ):void {}
	public function methodWithParametersAndReturnType( a:Number, b:Number, c:Number ):Number { return a + b + c; }
	public function methodWithOptionalParameters( a:Number, b:Number = 0, c:Number = 0 ):Boolean { return true; }
}